package org.budo.dubbo.protocol.async.config.annotation;

import org.budo.dubbo.protocol.async.config.BudoAsyncServiceBean;
import org.budo.dubbo.protocol.async.repository.AsyncRepository;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.spring.bean.factory.support.BeanBuilder;
import org.budo.support.spring.util.SpringUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 * 
 * @see org.springframework.web.servlet.config.MvcNamespaceHandler
 * @see org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
 * @see org.budo.spring.cache.interceptor.config.BudoSpringEhCacheInterceptorConfigurer
 * @see com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
 * @see com.alibaba.dubbo.config.spring.schema.AnnotationBeanDefinitionParser
 */
@Slf4j
public class BudoAsyncAnnotationDrivenBeanDefinitionParser implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("#20 postProcessBeanFactory, beanFactory=" + beanFactory); // 3
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ListableBeanFactory beanFactory = (ListableBeanFactory) registry;

        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName);
            String beanClassName = beanDefinition.getBeanClassName();

            if (null == beanClassName || beanClassName.isEmpty()) {
                continue;
            }

            Class beanType = ReflectUtil.classForName(beanClassName);

            AsyncService asyncService = AnnotationUtils.getAnnotation(beanType, AsyncService.class);
            if (null != asyncService) {
                log.info("#40 add BudoAsyncServiceBean for " + beanClassName);

                String asyncRepositoryBeanName = asyncService.repository();
                if (StringUtils.isEmpty(asyncRepositoryBeanName)) {
                    String[] beanNames = beanFactory.getBeanNamesForType(AsyncRepository.class);
                    if (null == beanNames || beanNames.length < 1 | beanNames.length > 1) {
                        throw new IllegalArgumentException("#58 you may try @AsyncService(repository=\"xxx\") for " + beanType);
                    }

                    asyncRepositoryBeanName = beanNames[0];
                }

                String instanceBeanName = SpringUtil.getNameByBeanType(beanFactory, beanClassName);

                new BeanBuilder() //
                        .id(instanceBeanName + "-BudoAsyncServiceBean") //
                        .type(BudoAsyncServiceBean.class) //
                        .ref("instance", instanceBeanName) //
                        .property("asyncRepositoryBeanName", asyncRepositoryBeanName) //
                        .registerTo(registry);
            }
        }
    }
}